package com.wmh.baseservice.common.shiro.realm;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wmh.baseservice.admin.pojo.AdInfo;
import com.wmh.baseservice.admin.pojo.AdRolePermissions;
import com.wmh.baseservice.admin.service.AdInfoService;
import com.wmh.baseservice.admin.service.AdRolePermissionsService;
import com.wmh.baseservice.common.ex.ServiceException;
import com.wmh.baseservice.common.redis.config.RedisConfig;
import com.wmh.baseservice.common.redis.utils.RedisUtil;
import com.wmh.baseservice.common.shiro.bean.JwtToken;
import com.wmh.baseservice.common.shiro.config.ShiroConfig;
import com.wmh.baseservice.common.shiro.jwt.JwtTokenUtil;
import com.wmh.baseservice.common.utils.CommUtil;
import com.wmh.baseservice.merchant.pojo.MerchantInfo;
import com.wmh.baseservice.merchant.pojo.MerchantRolePermissions;
import com.wmh.baseservice.merchant.service.MerchantInfoService;
import com.wmh.baseservice.merchant.service.MerchantRolePermissionsService;
import com.wmh.baseservice.user.pojo.UsInfo;
import com.wmh.baseservice.user.pojo.UsRolePermissions;
import com.wmh.baseservice.user.service.UsInfoService;
import com.wmh.baseservice.user.service.UsRolePermissionsService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.data.redis.RedisConnectionFailureException;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;


/**
 * @author xy
 * @Date: 2021-10-29 16:10
 * @describe JWT身份权限校验
 */
public class JwtRealm extends AuthorizingRealm {

    @Resource
    private UsInfoService usInfoService;
    @Resource
    private AdInfoService adInfoService;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private AdRolePermissionsService adRolePermissionsService;
    @Resource
    private UsRolePermissionsService usRolePermissionsService;
    @Resource
    private MerchantInfoService merchantInfoService;
    @Resource
    private MerchantRolePermissionsService merchantRolePermissionsService;
    @Resource
    private HttpServletRequest request;


    /*
     * 标识这个Realm是专门用来验证JwtToken
     * 不负责验证其他的token（UsernamePasswordToken）
     * */
    @Override
    public boolean supports(AuthenticationToken token) {
        //这个token就是从过滤器中传入的jwtToken
        return token instanceof JwtToken;
    }

    //权限
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        String userName = SecurityUtils.getSubject().getPrincipal().toString();
        //获得该用户角色
        if (userName.contains(ShiroConfig.SHIRO_ADMIN)) {
            Long adId = (Long) request.getAttribute(AdInfo.ID);
            return getRoleOrPermissions(adId, ShiroConfig.SHIRO_ADMIN, info);
        } else if (userName.contains(ShiroConfig.SHIRO_USER) || userName.contains(ShiroConfig.SHIRO_SSO)) {
            Long usId = (Long) request.getAttribute("usId");
            return getRoleOrPermissions(usId, ShiroConfig.SHIRO_USER, info);
        } else if (userName.contains(ShiroConfig.SHIRO_MERCHANT)) {
            Long merchantId = (Long) request.getAttribute(MerchantInfo.ID);
            return getRoleOrPermissions(merchantId, ShiroConfig.SHIRO_MERCHANT, info);
        } else {
            throw new UnauthorizedException();
        }
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String jwt = (String) token.getPrincipal();
        if (!CommUtil.checkNull(jwt)) {
            throw new ServiceException("jwtToken 不允许为空");
        }
        if (!JwtTokenUtil.isTokenExpired(jwt)) {
            throw new UnauthenticatedException();
        }
        //验证user是否存在
        String users = JwtTokenUtil.getUserInfoFromToken(jwt).get("account").toString();
        String[] usersSplit = users.split(":");
        if (usersSplit.length != 2) {
            throw new UnauthenticatedException();
        }
        String username = usersSplit[0];
        String password;
        if (username.contains(ShiroConfig.SHIRO_USER) || username.contains(ShiroConfig.SHIRO_SSO)) {
            UsInfo usInfo;
            if (username.contains(ShiroConfig.SHIRO_USER)) {
                //账号(手机号)密码
                usInfo = usInfoService.getOneForUpdate(new QueryWrapper<UsInfo>().eq(UsInfo.PHONE, username.replace(ShiroConfig.SHIRO_USER, "")));
            } else {
                usInfo = usInfoService.getOneForUpdate(new QueryWrapper<UsInfo>().eq(UsInfo.OPEN_ID, username.replace(ShiroConfig.SHIRO_SSO, "")));
            }
            if (!CommUtil.checkNull(usInfo)) {
                throw new ServiceException("账号不存在");
            }
            password = usInfo.getPassword();
            request.setAttribute("usId", usInfo.getId());
        } else if (username.contains(ShiroConfig.SHIRO_ADMIN)) {
            //管理员
            AdInfo adInfo = adInfoService.getOneForUpdate(new QueryWrapper<AdInfo>().eq(AdInfo.ACCOUNT, username.replace(ShiroConfig.SHIRO_ADMIN, "")));
            if (!CommUtil.checkNull(adInfo)) {
                throw new ServiceException("账号不存在");
            }
            password = adInfo.getPassword();
            request.setAttribute(AdInfo.ID, adInfo.getId());
        } else if (username.contains(ShiroConfig.SHIRO_MERCHANT)) {
            String replace = username.replace(ShiroConfig.SHIRO_MERCHANT, "");
            QueryWrapper<MerchantInfo> eq = new QueryWrapper<MerchantInfo>().eq(MerchantInfo.ACCOUNT, replace);
            MerchantInfo merchantInfo = merchantInfoService.getOneForUpdate(eq);
            if (!CommUtil.checkNull(merchantInfo)) {
                throw new ServiceException("账号不存在");
            }
            password = merchantInfo.getPassword();
            request.setAttribute(MerchantInfo.ID, merchantInfo.getId());
        } else {
            throw new ServiceException("身份错误");
        }
        return new SimpleAuthenticationInfo(users, password, JwtRealm.class.getName());
    }

    /**
     * @param id     账号id
     * @param role   角色后缀
     * @param simple 权限对象
     */
    public SimpleAuthorizationInfo getRoleOrPermissions(Long id, String role, SimpleAuthorizationInfo simple) {
        //项目名，区分多项目
        String project = RedisConfig.KEY_INFO;
        String key = "role_permissions_" + id + role + project;
        try {
            simple = (SimpleAuthorizationInfo) redisUtil.get(key);
            if (!CommUtil.checkNull(simple)) {
                //查询所有权限
                if (role.contains(ShiroConfig.SHIRO_ADMIN)) {
                    simple = getSimpleAuthorizationInfo(id, 1);
                    redisUtil.set(key, simple);
                } else if (role.contains(ShiroConfig.SHIRO_USER)) {
                    //用户权限
                    simple = getSimpleAuthorizationInfo(id, 2);
                    redisUtil.set(key, simple);
                } else if (role.contains(ShiroConfig.SHIRO_MERCHANT)) {
                    //商家权限
                    simple = getSimpleAuthorizationInfo(id, 3);
                    redisUtil.set(key, simple);
                }
            }
        } catch (RedisConnectionFailureException e) {
            simple = getSimpleAuthorizationInfo(id, role.contains(ShiroConfig.SHIRO_ADMIN) ? 1 : (role.contains(ShiroConfig.SHIRO_MERCHANT) ? 3 : 2));
        }
        return simple;
    }


    private SimpleAuthorizationInfo getSimpleAuthorizationInfo(Long id, Integer type) {
        SimpleAuthorizationInfo simple;
        Set<String> roles = new LinkedHashSet<>();
        Set<String> permissions = new LinkedHashSet<>();
        if (type == 1) {
            List<AdRolePermissions> adRolePermissions = adRolePermissionsService.listForUpdate(new QueryWrapper<AdRolePermissions>()
                    .eq(AdRolePermissions.AD_ID, id));
            roles = adRolePermissions.stream().filter(item -> CommUtil.checkNull(item.getRole())).map(AdRolePermissions::getRole).collect(Collectors.toSet());
            permissions = adRolePermissions.stream().filter(item -> CommUtil.checkNull(item.getPermissions())).map(AdRolePermissions::getPermissions).collect(Collectors.toSet());
        } else if (type == 2) {
            List<UsRolePermissions> usRolePermissionsList = usRolePermissionsService.listForUpdate(new QueryWrapper<UsRolePermissions>()
                    .eq(UsRolePermissions.US_ID, id));
            roles = usRolePermissionsList.stream().filter(item -> CommUtil.checkNull(item.getRole())).map(UsRolePermissions::getRole).collect(Collectors.toSet());
            permissions = usRolePermissionsList.stream().filter(item -> CommUtil.checkNull(item.getPermissions())).map(UsRolePermissions::getPermissions).collect(Collectors.toSet());
        } else if (type == 3) {
            List<MerchantRolePermissions> usRolePermissionsList = merchantRolePermissionsService.listForUpdate(new QueryWrapper<MerchantRolePermissions>()
                    .eq(MerchantRolePermissions.MERCHANT_ID, id));
            roles = usRolePermissionsList.stream().filter(item -> CommUtil.checkNull(item.getRole())).map(MerchantRolePermissions::getRole).collect(Collectors.toSet());
            permissions = usRolePermissionsList.stream().filter(item -> CommUtil.checkNull(item.getPermissions())).map(MerchantRolePermissions::getPermissions).collect(Collectors.toSet());
        }
        simple = new SimpleAuthorizationInfo();
        simple.setRoles(roles);
        simple.setStringPermissions(permissions);
        return simple;
    }
}
